#!/usr/bin/env python
#
# get_pkg_depends.py
#
# A helper script to print run-time dependencies of given binary package
# with YAML format using `apt-cache depends`. User need to only select
# one package if multiple candidates are found as the dependency.
# i.e. OR packages (pkg1|pkg2|pkg3), virtual packages
#
# Usage:
#   $ ./get_pkg_depends.py buster libssl1.1 apt
#
# This script is used as the backend of generate-proposal.py, but
# can be used individually to get the dependency information
# when users create proposal.yml manually.
#
# Copyright (c) 2019 TOSHIBA Corporation
#
# SPDX-License-Identifier: Apache-2.0
#

import sys
import common

Q1 = "Choose the 'or' dependency package from the below list: "
Q2 = "Choose one of the virtual package provider: "
# prv_sel_memory_dict=dict(tuple(choose_list) : dict(selected_item : list(bin_pkg_set, is_exist_in_pkg_list)))
prv_sel_memory_dict = dict()
pkg_list_sel = set()


def load_prv_sel_pkg_list(pkg_list_info):
    """
    Read all the binary packages and dependencies from pkglist_<codename>.yml file
    :param pkg_list_info:
    :return:
    """
    global pkg_list_sel
    pkg_list_dict = pkg_list_info.get_pdp_info()
    pkg_list_sel = set()
    for sp_name, sp_data in pkg_list_dict.items():
        for bp_name, bp_data in sp_data.bin_pkg_data_dict.items():
            pkg_list_sel.add(bp_name)
            # Save all 'or' and 'virtual' list selections
            for dp in bp_data.bin_pkg_depends:
                if dp.startswith('<') and dp.partition('>:')[2]:
                    pkg_list_sel.add(dp.partition('>:')[0] + ">")
                    pkg_list_sel.add(dp.partition('>:')[2])
                else:
                    pkg_list_sel.add(dp)


def get_prv_selection(dep_list):
    """
    Searches whether the given list is already chosen by user previously and returns the chosen information
    :param dep_list: choose list
    :return: dictionary of chosen item to list of package names
    """
    global prv_sel_memory_dict
    if dep_list in prv_sel_memory_dict:
        prv_sel_info = prv_sel_memory_dict[dep_list]
    else:
        prv_sel_info = dict()

    for dp in dep_list:
        if dp not in prv_sel_info:
            prv_sel_info[dp] = [set(), False]
        if dp in pkg_list_sel:
            prv_sel_info[dp][1] = True

    return prv_sel_info


def save_selection(dep_list, dep_sel, bin_pkg_name):
    """
    Saves the current selection
    :param dep_list: choose list
    :param dep_sel: selected item
    :param bin_pkg_name: debian package for which it is selected
    :return: None
    """
    global prv_sel_memory_dict
    if dep_list in prv_sel_memory_dict:
        if dep_sel not in prv_sel_memory_dict[dep_list]:
            prv_sel_memory_dict[dep_list][dep_sel] = [set(), False]
    else:
        prv_sel_memory_dict[dep_list] = {dep_sel: [set(), False]}

    prv_sel_memory_dict[dep_list][dep_sel][0].add(bin_pkg_name)


def get_pkg_depends(pkg_name, apt):
    """
    provide the debian package dependencies with selected 'or' package and virtual package
    :param pkg_name: binary package name
    :param apt: apt object
    :return: return list of package dependencies
    """
    dp_set, dp_vir_pkg_dict = apt.apt_cache_get_depends_list(pkg_name)
    pkg_depends_set = set()
    for dp in dp_set:
        if len(dp) == 1:
            # it is a single dependency package
            if dp[0] in dp_vir_pkg_dict:
                # it is virtual package
                if len(dp_vir_pkg_dict[dp[0]]):
                    # if there are providers list then ask user to choose from the providers list
                    sel_vir_pkg_name = common.input_choose_radio(Q2 + dp[0], dp_vir_pkg_dict[dp[0]],
                                                                 prv_sel_info_dict=get_prv_selection(
                                                                     dp_vir_pkg_dict[dp[0]]))
                    save_selection(dp_vir_pkg_dict[dp[0]], sel_vir_pkg_name, pkg_name)
                    pkg_depends_set.add(sel_vir_pkg_name)
                else:
                    # some virtual package may not have providers list, in that case select virtual package name itself
                    pkg_depends_set.add(dp[0])

            else:
                # it is normal dependency package
                pkg_depends_set.add(dp[0])
        else:
            # it is 'or' package list
            sel_or_pkg_name = common.input_choose_radio(Q1, dp, prv_sel_info_dict=get_prv_selection(dp))
            save_selection(dp, sel_or_pkg_name, pkg_name)
            if sel_or_pkg_name in dp_vir_pkg_dict:
                # if selected package is virtual
                if len(dp_vir_pkg_dict[sel_or_pkg_name]):
                    # if there are providers list then ask user to choose from the providers list
                    sel_vir_pkg_name = common.input_choose_radio(Q2 + sel_or_pkg_name, dp_vir_pkg_dict[sel_or_pkg_name],
                                                                 prv_sel_info_dict=get_prv_selection(dp_vir_pkg_dict[sel_or_pkg_name]))
                    save_selection(dp_vir_pkg_dict[sel_or_pkg_name], sel_vir_pkg_name, pkg_name)
                    pkg_depends_set.add(sel_vir_pkg_name)
                else:
                    # some virtual package may not have providers list, in that case select virtual package name itself
                    pkg_depends_set.add(sel_or_pkg_name)
            else:
                pkg_depends_set.add(sel_or_pkg_name)

    return sorted(pkg_depends_set)


def usage():
    print("Usage:  get-pkg-depends <codename> pkg1 [pkg2 ...]")
    print("\n Provides the debian package dependencies and also it ask user to choose 'or' package or virtual "
          "package provider")
    exit(1)


def main(argv):
    apt = common.Apt()
    try:
        if len(argv) <= 1:
            usage()

        if argv[0] in common.DEBIAN_CODE_NAMES:
            # Initialize apt
            if not apt.apt_initialize(argv[0]):
                del apt
                common.die("Apt initialize is failed")

            pdp_info = common.PDPInfo(argv[0])
            pdp_info.load_pdp()
            load_prv_sel_pkg_list(pdp_info)
            # process the list
            yaml_dict = {}
            for pkg in argv[1:]:
                # Check duplicate entries
                if pkg in yaml_dict:
                    continue

                # Check if given package is valid package or not
                src_pkg_name, src_pkg_ver, src_pkg_bin_list = apt.apt_cache_get_src_info(pkg)
                if pkg not in src_pkg_bin_list:
                    print(pkg + " is not a valid binary package")
                    continue

                # get binary package dependencies
                print("\ngetting package dependencies for " + pkg)
                yaml_dict[pkg] = get_pkg_depends(pkg, apt)

            if len(yaml_dict):
                print("\nDependencies list of binary packages:")
            # Print output in YAML format
            for k, v in yaml_dict.items():
                print(k + ":")
                print("\t-" + "\n\t-".join(v))
        else:
            print("code names must be one of the following list: " + ",".join(common.DEBIAN_CODE_NAMES))
            usage()
    finally:
        del apt


if __name__ == "__main__":
    main(sys.argv[1:])
